home *** CD-ROM | disk | FTP | other *** search
- #define USE_SPEAKSTRING false
-
- /* Segmentation strategy:
-
- This program consists of three segments. Main contains most of the code,
- including the MPW libraries, and the main program. Initialize contains
- code that is only used once, during startup, and can be unloaded after the
- program starts. %A5Init is automatically created by the Linker to initialize
- globals for the MPW libraries and is unloaded right away. */
-
-
- /* SetPort strategy:
-
- Toolbox routines do not change the current port. In spite of this, in this
- program we use a strategy of calling SetPort whenever we want to draw or
- make calls which depend on the current port. This makes us less vulnerable
- to bugs in other software which might alter the current port (such as the
- bug (feature?) in many desk accessories which change the port on OpenDeskAcc).
- Hopefully, this also makes the routines from this program more self-contained,
- since they don't depend on the current port setting. */
-
-
- /* Clipboard strategy:
-
- This program does not maintain a private scrap. Whenever a cut, copy, or paste
- occurs, we import/export from the public scrap to TextEdit's scrap right away,
- using the TEToScrap and TEFromScrap routines. If we did use a private scrap,
- the import/export would be in the activate/deactivate event and suspend/resume
- event routines. */
-
- /* A/UX is case sensitive, so use correct case for include file names */
- #ifndef THINK_C
- #include <types.h>
- #include <quickdraw.h>
- #include <fonts.h>
- #include <events.h>
- #include <controls.h>
- #include <windows.h>
- #include <menus.h>
- #include <textedit.h>
- #include <dialogs.h>
- #include <desk.h>
- #include <scrap.h>
- #include <toolutils.h>
- #include <memory.h>
- #include <segload.h>
- #include <files.h>
- #include <osutils.h>
- #include <osevents.h>
- #include <diskinit.h>
- #include <packages.h>
- #include <errors.h>
- #include <standardfile.h>
- #endif
- #include <traps.h>
- #include <values.h>
- #include "Speech.h" /* SPEECH MANAGER */
- #include "TESample.h" /* bring in all the #defines for TESample */
-
- /* A DocumentRecord contains the WindowRecord for one of our document windows,
- as well as the TEHandle for the text we are editing. Other document fields
- can be added to this record as needed. For a similar example, see how the
- Window Manager and Dialog Manager add fields after the GrafPort. */
- typedef struct {
- WindowRecord docWindow;
- TEHandle docTE;
- ControlHandle docVScroll;
- ControlHandle docHScroll;
- ProcPtr docClik;
- } DocumentRecord, *DocumentPeek;
-
-
-
- /* The "g" prefix is used to emphasize that a variable is global. */
-
- /* GMac is used to hold the result of a SysEnvirons call. This makes
- it convenient for any routine to check the environment. It is
- global information, anyway. */
- SysEnvRec gMac; /* set up by Initialize */
-
- /* GHasWaitNextEvent is set at startup, and tells whether the WaitNextEvent
- trap is available. If it is false, we know that we must call GetNextEvent. */
- Boolean gHasWaitNextEvent; /* set up by Initialize */
-
- /* GInBackground is maintained by our OSEvent handling routines. Any part of
- the program can check it to find out if it is currently in the background. */
- Boolean gInBackground; /* maintained by Initialize and DoEvent */
-
- /* GNumDocuments is used to keep track of how many open documents there are
- at any time. It is maintained by the routines that open and close documents. */
- short gNumDocuments; /* maintained by Initialize, DoNew, and DoCloseWindow */
-
- Handle menuBar;
-
- /* gSpeechChan is a global speech channel which can be used for speaking */
- /* gSpeechStr is a global pascal string which can be used for speech */
- /* gSpeechErr is a global error return which can be latched if desired */
-
- SpeechChannel gSpeechChan = NULL;
- Str255 gSpeechStr = "\pT E Sample Application";
- unsigned char gTextBuf [4096] = {0};
- unsigned short gTextBufLen = 0;
- OSErr gSpeechErr = noErr;
-
- VoiceSpec vspec[kMaxVoices];
- short voiceSel;
-
-
- /* gClickMultiple, gLastClickTickCount, and gLastClickWhere are used to support triple-click to select whole line */
- /* gClickMultiple increments every time a mouse down event occurs within the system's GetDblTime() */
- long gClickMultiple = 0; /* 0 - idle, 1 - 1st click, 2 - double click, 3 - triple click, etc. */
- long gLastClickTickCount = 0; /* tickcount at last mousedown */
- Point gLastClickWhere = {-32768, -32768}; /* global mouse position at last mouse down */
-
- /* Here are declarations for all of the C routines. In MPW 3.0 we can use
- actual prototypes for parameter type checking. A/UX C does not grok
- prototypes, so eliminate them under A/UX */
-
- extern pascal OSErr SM_InitializeSpeechManager( void ); /* SPEECH MANAGER: INIT startup -- allocates globals, installs dispatcher */
-
- void AlertUser( short error );
- void EventLoop( void );
- void DoEvent( EventRecord *event );
- void AdjustCursor( Point mouse, RgnHandle region );
- void GetGlobalMouse( Point *mouse );
- void DoGrowWindow( WindowPtr window, EventRecord *event );
- void DoZoomWindow( WindowPtr window, short part );
- void ResizeWindow( WindowPtr window );
- void GetLocalUpdateRgn( WindowPtr window, RgnHandle localRgn );
- void DoUpdate( WindowPtr window );
- //void DoDeactivate( WindowPtr window );
- void DoActivate( WindowPtr window, Boolean becomingActive );
- void DoContentClick( WindowPtr window, EventRecord *event );
- void DoKeyDown( EventRecord *event );
- unsigned long GetSleep( void );
- void CommonAction( ControlHandle control, short *amount );
- pascal void VActionProc( ControlHandle control, short part );
- pascal void HActionProc( ControlHandle control, short part );
- void DoIdle( void );
- void DrawWindow( WindowPtr window );
- void AdjustMenus( void );
- void DoMenuCommand( long menuResult );
- Boolean DoNew( void );
- void DoOpen( void );
- Boolean DoCloseWindow( WindowPtr window );
- void Terminate( void );
- void Initialize( void );
- void BigBadError( short error );
- void GetTERect( WindowPtr window, Rect *teRect );
- void AdjustViewRect( TEHandle docTE );
- void AdjustTE( WindowPtr window );
- void AdjustHV( Boolean isVert, ControlHandle control, TEHandle docTE,
- Boolean canRedraw );
- void AdjustScrollValues( WindowPtr window, Boolean canRedraw );
- void AdjustScrollSizes( WindowPtr window );
- void AdjustScrollbars( WindowPtr window, Boolean needsResize );
- pascal void PASCALCLIKLOOP(void);
- pascal ProcPtr GETOLDCLIKLOOP(void);
- Boolean IsAppWindow( WindowPtr window );
- Boolean IsDAWindow( WindowPtr window );
- Boolean TrapAvailable( short tNumber, TrapType tType );
-
- long TrackMultipleClicks(long when, Point where);
- short TELineStartOffset( TEHandle te, short theLine);
- short TECharOffsetToLine(TEHandle te, short charOffset);
- void TESelectLine(TEHandle te, short theLine);
- OSErr SpeakTextRange(TEHandle te, short sIndex, short eIndex);
- void StartupSpeech( void );
-
-
- /* Define HiWrd and LoWrd macros for efficiency. */
- #define HiWrd(aLong) (((aLong) >> 16) & 0xFFFF)
- #define LoWrd(aLong) ((aLong) & 0xFFFF)
-
- /* Define TopLeft and BotRight macros for convenience. Notice the implicit
- dependency on the ordering of fields within a Rect */
- #define TopLeft(aRect) (* (Point *) &(aRect).top)
- #define BotRight(aRect) (* (Point *) &(aRect).bottom)
-
-
- /* This routine is part of the MPW runtime library. This external
- reference to it is done so that we can unload its segment, %A5Init. */
-
- extern void _DataInit( void );
-
-
- /* A reference to our assembly language routine that gets attached to the clikLoop
- field of our TE record. */
-
- extern pascal void ASMCLIKLOOP();
-
- #pragma segment Main
- main()
- {
- //DebugStr("\pAt Main entry point for TESample…");
-
- #ifndef THINK_C
- UnloadSeg((Ptr) _DataInit); /* note that _DataInit must not be in Main! */
- #endif
-
- /* 1.01 - call to ForceEnvirons removed */
-
- /* If you have stack requirements that differ from the default,
- then you could use SetApplLimit to increase StackSpace at
- this point, before calling MaxApplZone. */
- MaxApplZone(); /* expand the heap so code segments load at the top */
-
- Initialize(); /* initialize the program */
-
- UnloadSeg((Ptr) Initialize); /* note that Initialize must not be in Main! */
-
-
- StartupSpeech();
-
-
- EventLoop(); /* call the main event loop */
- }
-
-
- /* Get events forever, and handle them by calling DoEvent.
- Also call AdjustCursor each time through the loop. */
-
- #pragma segment Main
- void EventLoop( void )
- {
- RgnHandle cursorRgn;
- Boolean gotEvent;
- EventRecord event;
- Point mouse;
-
- cursorRgn = NewRgn(); /* we’ll pass WNE an empty region the 1st time thru */
- do {
- /* use WNE if it is available */
- if ( gHasWaitNextEvent ) {
- GetGlobalMouse(&mouse);
- AdjustCursor(mouse, cursorRgn);
- gotEvent = WaitNextEvent(everyEvent, &event, GetSleep(), cursorRgn);
- }
- else {
- SystemTask();
- gotEvent = GetNextEvent(everyEvent, &event);
- }
- if ( gotEvent ) {
- /* make sure we have the right cursor before handling the event */
- AdjustCursor(event.where, cursorRgn);
- DoEvent(&event);
- } else DoIdle();
- /* If you are using modeless dialogs that have editText items,
- you will want to call IsDialogEvent to give the caret a chance
- to blink, even if WNE/GNE returned FALSE. However, check FrontWindow
- for a non-NIL value before calling IsDialogEvent. */
- } while ( true ); /* loop forever; we quit via ExitToShell */
- } /*EventLoop*/
-
-
- /* Do the right thing for an event. Determine what kind of event it is, and call
- the appropriate routines. */
-
- #pragma segment Main
- void DoEvent( EventRecord *event )
- {
- short part, err;
- WindowPtr window;
- unsigned char key;
- Point aPoint;
-
- switch ( event->what ) {
- case nullEvent:
- /* we idle for null/mouse moved events ands for events which aren’t
- ours (see EventLoop) */
- DoIdle();
- break;
- case mouseDown:
- part = FindWindow(event->where, &window);
- switch ( part ) {
- case inMenuBar: /* process a mouse menu command (if any) */
- AdjustMenus(); /* bring ’em up-to-date */
- DoMenuCommand(MenuSelect(event->where));
- break;
- case inSysWindow: /* let the system handle the mouseDown */
- SystemClick(event, window);
- break;
- case inContent:
- if ( window != FrontWindow() ) {
- SelectWindow(window);
- /*DoEvent(event);*/ /* use this line for "do first click" */
- } else
- DoContentClick(window, event);
- break;
- case inDrag: /* pass screenBits.bounds to get all gDevices */
- DragWindow(window, event->where, &qd.screenBits.bounds);
- break;
- case inGoAway:
- if ( TrackGoAway(window, event->where) )
- DoCloseWindow(window); /* we don’t care if the user cancelled */
- break;
- case inGrow:
- DoGrowWindow(window, event);
- break;
- case inZoomIn:
- case inZoomOut:
- if ( TrackBox(window, event->where, part) )
- DoZoomWindow(window, part);
- break;
- }
- break;
- case keyDown:
- case autoKey: /* check for menukey equivalents */
- key = (unsigned char) event->message & charCodeMask;
- if ( event->modifiers & cmdKey ) { /* Command key down */
- if ( event->what == keyDown ) {
- AdjustMenus(); /* enable/disable/check menu items properly */
- DoMenuCommand(MenuKey(key));
- }
- } else
- DoKeyDown(event);
- break;
- case activateEvt:
- DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);
- break;
- case updateEvt:
- DoUpdate((WindowPtr) event->message);
- break;
- /* 1.01 - It is not a bad idea to at least call DIBadMount in response
- to a diskEvt, so that the user can format a floppy. */
- case diskEvt:
- if ( HiWord(event->message) != noErr ) {
- SetPt(&aPoint, kDILeft, kDITop);
- err = DIBadMount(aPoint, event->message);
- }
- break;
- case kOSEvent:
- /* 1.02 - must BitAND with 0x0FF to get only low byte */
- switch ((event->message >> 24) & 0x0FF) { /* high byte of message */
- case kMouseMovedMessage:
- DoIdle(); /* mouse-moved is also an idle event */
- break;
- case kSuspendResumeMessage: /* suspend/resume is also an activate/deactivate */
- gInBackground = (event->message & kResumeMask) == 0;
- DoActivate(FrontWindow(), !gInBackground);
- break;
- }
- break;
- }
- } /*DoEvent*/
-
-
- /* Change the cursor's shape, depending on its position. This also calculates the region
- where the current cursor resides (for WaitNextEvent). When the mouse moves outside of
- this region, an event is generated. If there is more to the event than just
- “the mouse moved”, we get called before the event is processed to make sure
- the cursor is the right one. In any (ahem) event, this is called again before we
- fall back into WNE. */
-
- #pragma segment Main
- void AdjustCursor( Point mouse, RgnHandle region )
- {
- WindowPtr window;
- RgnHandle arrowRgn;
- RgnHandle iBeamRgn;
- Rect iBeamRect;
-
- window = FrontWindow(); /* we only adjust the cursor when we are in front */
- if ( (! gInBackground) && (! IsDAWindow(window)) ) {
- /* calculate regions for different cursor shapes */
- arrowRgn = NewRgn();
- iBeamRgn = NewRgn();
-
- /* start arrowRgn wide open */
- SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
-
- /* calculate iBeamRgn */
- if ( IsAppWindow(window) ) {
- iBeamRect = (*((DocumentPeek) window)->docTE)->viewRect;
- SetPort(window); /* make a global version of the viewRect */
- LocalToGlobal(&TopLeft(iBeamRect));
- LocalToGlobal(&BotRight(iBeamRect));
- RectRgn(iBeamRgn, &iBeamRect);
- /* we temporarily change the port’s origin to “globalfy” the visRgn */
- SetOrigin(-window->portBits.bounds.left, -window->portBits.bounds.top);
- SectRgn(iBeamRgn, window->visRgn, iBeamRgn);
- SetOrigin(0, 0);
- }
-
- /* subtract other regions from arrowRgn */
- DiffRgn(arrowRgn, iBeamRgn, arrowRgn);
-
- /* change the cursor and the region parameter */
- if ( PtInRgn(mouse, iBeamRgn) ) {
- SetCursor(*GetCursor(iBeamCursor));
- CopyRgn(iBeamRgn, region);
- } else {
- SetCursor(&qd.arrow);
- CopyRgn(arrowRgn, region);
- }
-
- DisposeRgn(arrowRgn);
- DisposeRgn(iBeamRgn);
- }
- } /*AdjustCursor*/
-
-
- /* Get the global coordinates of the mouse. When you call OSEventAvail
- it will return either a pending event or a null event. In either case,
- the where field of the event record will contain the current position
- of the mouse in global coordinates and the modifiers field will reflect
- the current state of the modifiers. Another way to get the global
- coordinates is to call GetMouse and LocalToGlobal, but that requires
- being sure that thePort is set to a valid port. */
-
- #pragma segment Main
- void GetGlobalMouse( Point *mouse )
- {
- EventRecord event;
-
- OSEventAvail(kNoEvents, &event); /* we aren't interested in any events */
- *mouse = event.where; /* just the mouse position */
- } /*GetGlobalMouse*/
-
-
- /* Called when a mouseDown occurs in the grow box of an active window. In
- order to eliminate any 'flicker', we want to invalidate only what is
- necessary. Since ResizeWindow invalidates the whole portRect, we save
- the old TE viewRect, intersect it with the new TE viewRect, and
- remove the result from the update region. However, we must make sure
- that any old update region that might have been around gets put back. */
-
- #pragma segment Main
- void DoGrowWindow( WindowPtr window, EventRecord *event )
- {
- long growResult;
- Rect tempRect;
- RgnHandle tempRgn;
- DocumentPeek doc;
-
- tempRect = qd.screenBits.bounds; /* set up limiting values */
- tempRect.left = kMinDocDim;
- tempRect.top = kMinDocDim;
- growResult = GrowWindow(window, event->where, &tempRect);
- /* see if it really changed size */
- if ( growResult != 0 ) {
- doc = (DocumentPeek) window;
- tempRect = (*doc->docTE)->viewRect; /* save old text box */
- tempRgn = NewRgn();
- GetLocalUpdateRgn(window, tempRgn); /* get localized update region */
- SizeWindow(window, LoWrd(growResult), HiWrd(growResult), true);
- ResizeWindow(window);
- /* calculate & validate the region that hasn’t changed so it won’t get redrawn */
- SectRect(&tempRect, &(*doc->docTE)->viewRect, &tempRect);
- ValidRect(&tempRect); /* take it out of update */
- InvalRgn(tempRgn); /* put back any prior update */
- DisposeRgn(tempRgn);
- }
- } /* DoGrowWindow */
-
-
- /* Called when a mouseClick occurs in the zoom box of an active window.
- Everything has to get re-drawn here, so we don't mind that
- ResizeWindow invalidates the whole portRect. */
-
- #pragma segment Main
- void DoZoomWindow( WindowPtr window, short part )
- {
- EraseRect(&window->portRect);
- ZoomWindow(window, part, window == FrontWindow());
- ResizeWindow(window);
- } /* DoZoomWindow */
-
-
- /* Called when the window has been resized to fix up the controls and content. */
- #pragma segment Main
- void ResizeWindow( WindowPtr window )
- {
- AdjustScrollbars(window, true);
- AdjustTE(window);
- InvalRect(&window->portRect);
- } /* ResizeWindow */
-
-
- /* Returns the update region in local coordinates */
- #pragma segment Main
- void GetLocalUpdateRgn( WindowPtr window, RgnHandle localRgn )
- {
- CopyRgn(((WindowPeek) window)->updateRgn, localRgn); /* save old update region */
- OffsetRgn(localRgn, window->portBits.bounds.left, window->portBits.bounds.top);
- } /* GetLocalUpdateRgn */
-
-
- /* This is called when an update event is received for a window.
- It calls DrawWindow to draw the contents of an application window.
- As an efficiency measure that does not have to be followed, it
- calls the drawing routine only if the visRgn is non-empty. This
- will handle situations where calculations for drawing or drawing
- itself is very time-consuming. */
-
- #pragma segment Main
- void DoUpdate( WindowPtr window )
- {
- if ( IsAppWindow(window) ) {
- BeginUpdate(window); /* this sets up the visRgn */
- if ( ! EmptyRgn(window->visRgn) ) /* draw if updating needs to be done */
- DrawWindow(window);
- EndUpdate(window);
- }
- } /*DoUpdate*/
-
-
- /* This is called when a window is activated or deactivated.
- It calls TextEdit to deal with the selection. */
-
- #pragma segment Main
- void DoActivate( WindowPtr window, Boolean becomingActive )
- {
- RgnHandle tempRgn, clipRgn;
- Rect growRect;
- DocumentPeek doc;
-
- if ( IsAppWindow(window) ) {
- doc = (DocumentPeek) window;
- if ( becomingActive ) {
- /* since we don’t want TEActivate to draw a selection in an area where
- we’re going to erase and redraw, we’ll clip out the update region
- before calling it. */
- tempRgn = NewRgn();
- clipRgn = NewRgn();
- GetLocalUpdateRgn(window, tempRgn); /* get localized update region */
- GetClip(clipRgn);
- DiffRgn(clipRgn, tempRgn, tempRgn); /* subtract updateRgn from clipRgn */
- SetClip(tempRgn);
- TEActivate(doc->docTE);
- SetClip(clipRgn); /* restore the full-blown clipRgn */
- DisposeRgn(tempRgn);
- DisposeRgn(clipRgn);
-
- /* the controls must be redrawn on activation: */
- (*doc->docVScroll)->contrlVis = kControlVisible;
- (*doc->docHScroll)->contrlVis = kControlVisible;
- InvalRect(&(*doc->docVScroll)->contrlRect);
- InvalRect(&(*doc->docHScroll)->contrlRect);
- /* the growbox needs to be redrawn on activation: */
- growRect = window->portRect;
- /* adjust for the scrollbars */
- growRect.top = (short) (growRect.bottom - kScrollbarAdjust);
- growRect.left = (short) (growRect.right - kScrollbarAdjust);
- InvalRect(&growRect);
- }
- else {
- TEDeactivate(doc->docTE);
- /* the controls must be hidden on deactivation: */
- HideControl(doc->docVScroll);
- HideControl(doc->docHScroll);
- /* the growbox should be changed immediately on deactivation: */
- DrawGrowIcon(window);
- }
- }
- } /*DoActivate*/
-
-
- /* This is called when a mouseDown occurs in the content of a window. */
-
- #pragma segment Main
- void DoContentClick( WindowPtr window, EventRecord *event )
- {
- Point mouse;
- ControlHandle control;
- short part, value;
- Boolean shiftDown;
- DocumentPeek doc;
- Rect teRect;
- long multiClicks;
- short charOffset;
- short theLine;
-
- multiClicks = TrackMultipleClicks(event->when, event->where);
-
- if ( IsAppWindow(window) ) {
- SetPort(window);
- mouse = event->where; /* get the click position */
- GlobalToLocal(&mouse);
- doc = (DocumentPeek) window;
- /* see if we are in the viewRect. if so, we won’t check the controls */
- GetTERect(window, &teRect);
- if ( PtInRect(mouse, &teRect) ) {
- /* see if we need to extend the selection */
- shiftDown = (event->modifiers & shiftKey) != 0; /* extend if Shift is down */
-
- if (shiftDown || multiClicks < 3) /* let TextEdit handle hard ones */
- TEClick(mouse, shiftDown, doc->docTE);
- else /* SPEECH MANAGER: we do the easier ones */
- {
- if (multiClicks == 3) /* triple-click: select entire line */
- {
- charOffset = TEGetOffset(mouse, doc->docTE);
-
- theLine = TECharOffsetToLine(doc->docTE, charOffset);
-
- if (theLine == (*(doc->docTE))->nLines) /* special case for beyond end of text */
- theLine--;
-
- TESelectLine(doc->docTE, theLine);
- }
- else if (multiClicks == 4) /* quadruple-click: select it all */
- TESetSelect(0, 32767, doc->docTE);
- }
- } else {
- part = FindControl(mouse, window, &control);
- switch ( part ) {
- case 0: /* do nothing for viewRect case */
- break;
- case inThumb:
- value = GetCtlValue(control);
- part = TrackControl(control, mouse, nil);
- if ( part != 0 ) {
- value -= GetCtlValue(control);
- /* value now has CHANGE in value; if value changed, scroll */
- if ( value != 0 )
- if ( control == doc->docVScroll )
- TEScroll(0, value * (*doc->docTE)->lineHeight, doc->docTE);
- else
- TEScroll(value, 0, doc->docTE);
- }
- break;
- default: /* they clicked in an arrow, so track & scroll */
- if ( control == doc->docVScroll )
- value = TrackControl(control, mouse, (ProcPtr) VActionProc);
- else
- value = TrackControl(control, mouse, (ProcPtr) HActionProc);
- break;
- }
- }
- }
- } /*DoContentClick*/
-
-
- /* This is called for any keyDown or autoKey events, except when the
- Command key is held down. It looks at the frontmost window to decide what
- to do with the key typed. */
-
- #pragma segment Main
- void DoKeyDown( EventRecord *event )
- {
- WindowPtr window;
- unsigned char key;
- TEHandle te;
- short sIndex, eIndex;
- short theLine;
- OSErr err;
-
- window = FrontWindow();
- if ( IsAppWindow(window) ) {
- te = ((DocumentPeek) window)->docTE;
- key = (unsigned char) event->message & charCodeMask;
-
- if ( key == kEnterChar ) {
- /* SPEECH MANAGER: */
- /* If any text selected, speak it. Otherwise speak entire line. */
-
- sIndex = (*te)->selStart;
- eIndex = (*te)->selEnd;
-
- if (eIndex <= sIndex) /* nothing selected */
- { /* speak entire line insertion point is on */
- theLine = TECharOffsetToLine(te, sIndex);
-
- if (theLine == (*te)->nLines) /* special case for beyond end of text */
- theLine--;
-
- sIndex = TELineStartOffset(te, theLine);
- eIndex = TELineStartOffset(te, theLine + 1);
- }
-
- err = SpeakTextRange(te, sIndex, eIndex);
- }
- else if ( key == kDelChar ||
- (*te)->teLength - ((*te)->selEnd - (*te)->selStart) + 1 < kMaxTELength )
- {
- /* we have a char. for our window; see if we are still below TextEdit’s
- limit for the number of characters (but deletes are always rad) */
-
- TEKey(key, te);
- AdjustScrollbars(window, false);
- AdjustTE(window);
- }
- else
- AlertUser(eExceedChar);
- }
- } /*DoKeyDown*/
-
-
- /* Calculate a sleep value for WaitNextEvent. This takes into account the things
- that DoIdle does with idle time. */
-
- #pragma segment Main
- unsigned long GetSleep( void )
- {
- long sleep;
- WindowPtr window;
- TEHandle te;
-
- sleep = MAXLONG; /* default value for sleep */
- if ( !gInBackground ) {
- window = FrontWindow(); /* and the front window is ours... */
- if ( IsAppWindow(window) ) {
- te = ((DocumentPeek) (window))->docTE; /* and the selection is an insertion point... */
- if ( (*te)->selStart == (*te)->selEnd )
- sleep = GetCaretTime(); /* blink time for the insertion point */
- }
- }
- return sleep;
- } /*GetSleep*/
-
-
- /* Common algorithm for pinning the value of a control. It returns the actual amount
- the value of the control changed. Note the pinning is done for the sake of returning
- the amount the control value changed. */
-
- #pragma segment Main
- void CommonAction( ControlHandle control, short *amount )
- {
- short value, max;
-
- value = GetCtlValue(control); /* get current value */
- max = GetCtlMax(control); /* and maximum value */
- *amount = (short) (value - *amount);
- if ( *amount < 0 )
- *amount = 0;
- else if ( *amount > max )
- *amount = max;
- SetCtlValue(control, *amount);
- *amount = (short) (value - *amount); /* calculate the real change */
- } /* CommonAction */
-
-
- /* Determines how much to change the value of the vertical scrollbar by and how
- much to scroll the TE record. */
-
- #pragma segment Main
- pascal void VActionProc( ControlHandle control, short part )
- {
- short amount;
- WindowPtr window;
- TEPtr te;
-
- if ( part != 0 ) { /* if it was actually in the control */
- window = (*control)->contrlOwner;
- te = *((DocumentPeek) window)->docTE;
- switch ( part ) {
- case inUpButton:
- case inDownButton: /* one line */
- amount = 1;
- break;
- case inPageUp: /* one page */
- case inPageDown:
- amount = (short) (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
- break;
- }
- if ( (part == inDownButton) || (part == inPageDown) )
- amount = (short) -amount; /* reverse direction for a downer */
- CommonAction(control, &amount);
- if ( amount != 0 )
- TEScroll(0, amount * te->lineHeight, ((DocumentPeek) window)->docTE);
- }
- } /* VActionProc */
-
-
- /* Determines how much to change the value of the horizontal scrollbar by and how
- much to scroll the TE record. */
-
- #pragma segment Main
- pascal void HActionProc( ControlHandle control, short part )
- {
- short amount;
- WindowPtr window;
- TEPtr te;
-
- if ( part != 0 ) {
- window = (*control)->contrlOwner;
- te = *((DocumentPeek) window)->docTE;
- switch ( part ) {
- case inUpButton:
- case inDownButton: /* a few pixels */
- amount = kButtonScroll;
- break;
- case inPageUp: /* a page */
- case inPageDown:
- amount = (short) (te->viewRect.right - te->viewRect.left);
- break;
- }
- if ( (part == inDownButton) || (part == inPageDown) )
- amount = (short) -amount; /* reverse direction */
- CommonAction(control, &amount);
- if ( amount != 0 )
- TEScroll(amount, 0, ((DocumentPeek) window)->docTE);
- }
- } /* VActionProc */
-
-
- /* This is called whenever we get a null event et al.
- It takes care of necessary periodic actions. For this program, it calls TEIdle. */
-
- #pragma segment Main
- void DoIdle( void )
- {
- WindowPtr window;
-
- window = FrontWindow();
- if ( IsAppWindow(window) )
- TEIdle(((DocumentPeek) window)->docTE);
- } /*DoIdle*/
-
-
- /* Draw the contents of an application window. */
-
- #pragma segment Main
- void DrawWindow( WindowPtr window )
- {
- SetPort(window);
- EraseRect(&window->portRect);
- DrawControls(window);
- DrawGrowIcon(window);
- TEUpdate(&window->portRect, ((DocumentPeek) window)->docTE);
- } /*DrawWindow*/
-
-
- /* Enable and disable menus based on the current state.
- The user can only select enabled menu items. We set up all the menu items
- before calling MenuSelect or MenuKey, since these are the only times that
- a menu item can be selected. Note that MenuSelect is also the only time
- the user will see menu items. This approach to deciding what enable/
- disable state a menu item has the advantage of concentrating all
- the decision-making in one routine, as opposed to being spread throughout
- the application. Other application designs may take a different approach
- that may or may not be as valid. */
-
- #pragma segment Main
- void AdjustMenus( void )
- {
- WindowPtr window;
- MenuHandle menu;
- long offset;
- Boolean undo;
- Boolean cutCopyClear;
- Boolean paste;
- TEHandle te;
-
- window = FrontWindow();
-
- menu = GetMHandle(mFile);
- if ( gNumDocuments < kMaxOpenDocuments )
- {
- EnableItem(menu, iNew); /* New is enabled when we can open more documents */
- EnableItem(menu, iOpen); /* Open is enabled when we can open more documents */
- }
- else
- {
- DisableItem(menu, iNew);
- DisableItem(menu, iOpen);
- }
- if ( window != nil ) /* Close is enabled when there is a window to close */
- EnableItem(menu, iClose);
- else
- DisableItem(menu, iClose);
-
- menu = GetMHandle(mEdit);
- undo = false;
- cutCopyClear = false;
- paste = false;
- if ( IsDAWindow(window) ) {
- undo = true; /* all editing is enabled for DA windows */
- cutCopyClear = true;
- paste = true;
-
- DisableItem(menu, iSelectAll);
- } else if ( IsAppWindow(window) ) {
- te = ((DocumentPeek) window)->docTE;
- if ( (*te)->selStart < (*te)->selEnd )
- cutCopyClear = true;
- /* Cut, Copy, and Clear is enabled for app. windows with selections */
- if ( GetScrap(nil, 'TEXT', &offset) > 0)
- paste = true; /* if there’s any text in the clipboard, paste is enabled */
-
- EnableItem(menu, iSelectAll);
- }
-
- if ( undo )
- EnableItem(menu, iUndo);
- else
- DisableItem(menu, iUndo);
-
- if ( cutCopyClear ) {
- EnableItem(menu, iCut);
- EnableItem(menu, iCopy);
- EnableItem(menu, iClear);
- } else {
- DisableItem(menu, iCut);
- DisableItem(menu, iCopy);
- DisableItem(menu, iClear);
- }
-
- if ( paste )
- EnableItem(menu, iPaste);
- else
- DisableItem(menu, iPaste);
- } /*AdjustMenus*/
-
-
- /* This is called when an item is chosen from the menu bar (after calling
- MenuSelect or MenuKey). It does the right thing for each command. */
-
- #pragma segment Main
- void DoMenuCommand( long menuResult )
- {
- short menuID, menuItem;
- short itemHit, daRefNum;
- Str255 daName;
- OSErr saveErr;
- TEHandle te;
- WindowPtr window;
- Handle aHandle;
- long oldSize, newSize;
- long total, contig;
- short sIndex, eIndex;
- short theLine;
- OSErr err;
-
- window = FrontWindow();
- menuID = HiWord(menuResult); /* use macros for efficiency to... */
- menuItem = LoWord(menuResult); /* get menu item number and menu number */
- switch ( menuID ) {
- case mApple:
- switch ( menuItem ) {
- case iAbout: /* bring up alert for About */
- itemHit = Alert(rAboutAlert, nil);
- break;
- default: /* all non-About items in this menu are DAs et al */
- /* type Str255 is an array in MPW 3 */
- GetItem(GetMHandle(mApple), menuItem, daName);
- daRefNum = OpenDeskAcc(daName);
- break;
- }
- break;
- case mFile:
- switch ( menuItem ) {
- case iNew:
- (void) DoNew();
- break;
- case iOpen:
- DoOpen();
- break;
- case iClose:
- DoCloseWindow(FrontWindow()); /* ignore the result */
- break;
- case iQuit:
- Terminate();
- break;
- }
- break;
- case mEdit: /* call SystemEdit for DA editing & MultiFinder */
- if ( !SystemEdit(menuItem-1) ) {
- te = ((DocumentPeek) FrontWindow())->docTE;
- switch ( menuItem ) {
- case iCut:
- if ( ZeroScrap() == noErr ) {
- PurgeSpace(&total, &contig);
- if ((*te)->selEnd - (*te)->selStart + kTESlop > contig)
- AlertUser(eNoSpaceCut);
- else
- {
- TECut(te);
- if ( TEToScrap() != noErr ) {
- AlertUser(eNoCut);
- ZeroScrap();
- }
- }
- }
- break;
- case iCopy:
- if ( ZeroScrap() == noErr ) {
- TECopy(te); /* after copying, export the TE scrap */
- if ( TEToScrap() != noErr ) {
- AlertUser(eNoCopy);
- ZeroScrap();
- }
- }
- break;
- case iPaste: /* import the TE scrap before pasting */
- if ( TEFromScrap() == noErr ) {
- if ( TEGetScrapLen() + ((*te)->teLength -
- ((*te)->selEnd - (*te)->selStart)) > kMaxTELength )
- AlertUser(eExceedPaste);
- else {
- aHandle = (Handle) TEGetText(te);
- oldSize = GetHandleSize(aHandle);
- newSize = oldSize + TEGetScrapLen() + kTESlop;
- SetHandleSize(aHandle, newSize);
- saveErr = MemError();
- SetHandleSize(aHandle, oldSize);
- if (saveErr != noErr)
- AlertUser(eNoSpacePaste);
- else
- TEPaste(te);
- }
- }
- else
- AlertUser(eNoPaste);
- break;
- case iClear:
- TEDelete(te);
- break;
- case iSelectAll:
- TESetSelect(0, 32767, te);
- break;
- }
- AdjustScrollbars(window, false);
- AdjustTE(window);
- }
- break;
- case mSpeech: /* SPEECH MANAGER */
- te = ((DocumentPeek) FrontWindow())->docTE;
-
- switch ( menuItem ) {
- case iSpeakAll: /* If any text selected, speak it. Otherwise speak whole text record. */
- sIndex = (*te)->selStart;
- eIndex = (*te)->selEnd;
-
- if (eIndex <= sIndex) /* no text is currently selected */
- {
- sIndex = 0;
- eIndex = (*te)->teLength;
- }
-
- saveErr = SpeakTextRange(te, sIndex, eIndex);
- break;
-
- case iSpeakLine: /* If any text selected, speak it. Otherwise speak entire line. */
-
- sIndex = (*te)->selStart;
- eIndex = (*te)->selEnd;
-
- if (eIndex <= sIndex) /* nothing selected */
- { /* speak entire line insertion point is on */
- theLine = TECharOffsetToLine(te, sIndex);
-
- if (theLine == (*te)->nLines) /* special case for beyond end of text */
- theLine--;
-
- sIndex = TELineStartOffset(te, theLine);
- eIndex = TELineStartOffset(te, theLine + 1);
- }
-
- saveErr = SpeakTextRange(te, sIndex, eIndex);
- break;
-
- case iPauseSpeech:
- if (gSpeechChan)
- saveErr = PauseSpeechAt(gSpeechChan, kImmediate);
- break;
-
- case iContinueSpeech:
- if (gSpeechChan)
- saveErr = ContinueSpeech(gSpeechChan);
- break;
- }
- break;
-
-
- case mVoice: /* SPEECH MANAGER */
- if (menuItem)
- {
- if (gSpeechChan)
- {
- err = DisposeSpeechChannel(gSpeechChan);
- gSpeechChan = NULL;
- }
- voiceSel = menuItem-1;
- }
- break;
- }
- HiliteMenu(0); /* unhighlight what MenuSelect (or MenuKey) hilited */
- } /*DoMenuCommand*/
-
-
-
-
-
- /* Create a new document and window. */
-
- #pragma segment Main
- Boolean DoNew( void )
- {
- Boolean good = false;
- Ptr storage;
- WindowPtr window;
- Rect destRect, viewRect;
- DocumentPeek doc;
-
- storage = NewPtr(sizeof(DocumentRecord));
- if ( storage != nil ) {
- window = GetNewWindow(rDocWindow, storage, (WindowPtr) -1);
- if ( window != nil ) {
- gNumDocuments += 1; /* this will be decremented when we call DoCloseWindow */
- SetPort(window);
- doc = (DocumentPeek) window;
- GetTERect(window, &viewRect);
- destRect = viewRect;
- destRect.right = (short) (destRect.left + kMaxDocWidth);
- doc->docTE = TENew(&destRect, &viewRect);
- good = doc->docTE != nil; /* if TENew succeeded, we have a good document */
- if ( good ) { /* 1.02 - good document? — proceed */
- AdjustViewRect(doc->docTE);
- TEAutoView(true, doc->docTE);
- doc->docClik = (ProcPtr) (*doc->docTE)->clikLoop;
- (*doc->docTE)->clikLoop = (ClikLoopProcPtr) ASMCLIKLOOP;
- }
-
- if ( good ) { /* good document? — get scrollbars */
- doc->docVScroll = GetNewControl(rVScroll, window);
- good = (doc->docVScroll != nil);
- }
- if ( good) {
- doc->docHScroll = GetNewControl(rHScroll, window);
- good = (doc->docHScroll != nil);
- }
-
- if ( good ) { /* good? — adjust & draw the controls, draw the window */
- /* false to AdjustScrollValues means musn’t redraw; technically, of course,
- the window is hidden so it wouldn’t matter whether we called ShowControl or not. */
- AdjustScrollValues(window, false);
- ShowWindow(window);
- } else {
- DoCloseWindow(window); /* otherwise regret we ever created it... */
- AlertUser(eNoWindow); /* and tell user */
- }
- } else
- DisposPtr(storage); /* get rid of the storage if it is never used */
- }
-
- return good;
-
- } /*DoNew*/
-
- /* Open a document into a new window. */
-
- #pragma segment Main
- void DoOpen( void )
- {
- SFReply theReply;
- Point where = {20, 90};
- SFTypeList typeList = {'TEXT'};
- OSErr err;
- short fRefNum;
- long fileLen;
- Handle textHandle;
- DocumentPeek doc;
-
- SFGetFile(where, 0, 0, 1, typeList, 0, &theReply); /* get a text file */
-
- if (theReply.good)
- {
- err = FSOpen(theReply.fName, theReply.vRefNum, &fRefNum); /* Open the file */
- if (err) /* failed on open */
- {
- SysBeep(10); /* bomb-out */
- }
- else
- {
- err = GetEOF(fRefNum, &fileLen); /* find out how long file is */
- if (err) { SysBeep(10); }
-
- if (fileLen > kMaxTELength) /* limit to max len of TE Record */
- fileLen = kMaxTELength;
-
- textHandle = NewHandle(fileLen); /* get some memory to hold text */
- if (!textHandle) { SysBeep(10); }
-
- MoveHHi(textHandle);
- HLock(textHandle);
-
- err = FSRead(fRefNum, &fileLen, *textHandle); /* read in the text */
- if (err) { SysBeep(10); }
-
- if (DoNew()) /* was able to make a new window */
- {
- doc = (DocumentPeek) FrontWindow();
- TESetText(*textHandle, fileLen, doc->docTE); /* stuff text in TERecord */
- }
- else { SysBeep(10); }
-
- DisposeHandle(textHandle);
-
- FSClose(fRefNum); /* close the disk file */
- }
- }
- }
-
- /* Close a window. This handles desk accessory and application windows. */
-
- /* 1.01 - At this point, if there was a document associated with a
- window, you could do any document saving processing if it is 'dirty'.
- DoCloseWindow would return true if the window actually closed, i.e.,
- the user didn’t cancel from a save dialog. This result is handy when
- the user quits an application, but then cancels the save of a document
- associated with a window. */
-
- #pragma segment Main
- Boolean DoCloseWindow( WindowPtr window )
- {
- TEHandle te;
-
- if ( IsDAWindow(window) )
- CloseDeskAcc(((WindowPeek) window)->windowKind);
- else if ( IsAppWindow(window) ) {
- te = ((DocumentPeek) window)->docTE;
- if ( te != nil )
- TEDispose(te); /* dispose the TEHandle if we got far enough to make one */
- /* 1.01 - We used to call DisposeWindow, but that was technically
- incorrect, even though we allocated storage for the window on
- the heap. We should instead call CloseWindow to have the structures
- taken care of and then dispose of the storage ourselves. */
- CloseWindow(window);
- DisposPtr((Ptr) window);
- gNumDocuments -= 1;
- }
- return true;
- } /*DoCloseWindow*/
-
-
- /**************************************************************************************
- *** 1.01 DoCloseBehind(window) was removed ***
-
- 1.01 - DoCloseBehind was a good idea for closing windows when quitting
- and not having to worry about updating the windows, but it suffered
- from a fatal flaw. If a desk accessory owned two windows, it would
- close both those windows when CloseDeskAcc was called. When DoCloseBehind
- got around to calling DoCloseWindow for that other window that was already
- closed, things would go very poorly. Another option would be to have a
- procedure, GetRearWindow, that would go through the window list and return
- the last window. Instead, we decided to present the standard approach
- of getting and closing FrontWindow until FrontWindow returns NIL. This
- has a potential benefit in that the window whose document needs to be saved
- may be visible since it is the front window, therefore decreasing the
- chance of user confusion. For aesthetic reasons, the windows in the
- application should be checked for updates periodically and have the
- updates serviced.
- **************************************************************************************/
-
-
- /* Clean up the application and exit. We close all of the windows so that
- they can update their documents, if any. */
-
- /* 1.01 - If we find out that a cancel has occurred, we won't exit to the
- shell, but will return instead. */
-
- #pragma segment Main
- void Terminate( void )
- {
- WindowPtr aWindow;
- Boolean closed;
- OSErr err;
-
- closed = true;
- do {
- aWindow = FrontWindow(); /* get the current front window */
- if (aWindow != nil)
- closed = DoCloseWindow(aWindow); /* close this window */
- }
- while (closed && (aWindow != nil));
- if (closed)
- {
- if (gSpeechChan) /* SPEECH MANAGER: get rid of channel */
- {
- err = DisposeSpeechChannel(gSpeechChan);
-
- if (err != noErr)
- AlertUser(eSpeechManager); /* some kind of error */
- }
-
- ExitToShell(); /* exit if no cancellation */
- }
- } /*Terminate*/
-
-
- /* Set up the whole world, including global variables, Toolbox managers,
- menus, and a single blank document. */
-
- /* 1.01 - The code that used to be part of ForceEnvirons has been moved into
- this module. If an error is detected, instead of merely doing an ExitToShell,
- which leaves the user without much to go on, we call AlertUser, which puts
- up a simple alert that just says an error occurred and then calls ExitToShell.
- Since there is no other cleanup needed at this point if an error is detected,
- this form of error- handling is acceptable. If more sophisticated error recovery
- is needed, an exception mechanism, such as is provided by Signals, can be used. */
-
- #pragma segment Initialize
- void Initialize( void )
- {
- long total, contig;
- EventRecord event;
- short count;
-
- gInBackground = false;
-
- InitGraf((Ptr) &qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(nil);
- InitCursor();
-
- /* Call MPPOpen and ATPLoad at this point to initialize AppleTalk,
- if you are using it. */
- /* NOTE -- It is no longer necessary, and actually unhealthy, to check
- PortBUse and SPConfig before opening AppleTalk. The drivers are capable
- of checking for port availability themselves. */
-
- /* This next bit of code is necessary to allow the default button of our
- alert be outlined.
- 1.02 - Changed to call EventAvail so that we don't lose some important
- events. */
-
- for (count = 1; count <= 3; count++)
- EventAvail(everyEvent, &event);
-
- /* Ignore the error returned from SysEnvirons; even if an error occurred,
- the SysEnvirons glue will fill in the SysEnvRec. You can save a redundant
- call to SysEnvirons by calling it after initializing AppleTalk. */
-
- SysEnvirons(kSysEnvironsVersion, &gMac);
-
- /* Make sure that the machine has at least 128K ROMs. If it doesn't, exit. */
-
- if (gMac.machineType < 0) BigBadError(eWrongMachine);
-
- /* 1.02 - Move TrapAvailable call to after SysEnvirons so that we can tell
- in TrapAvailable if a tool trap value is out of range. */
-
- gHasWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
-
- /* 1.01 - We used to make a check for memory at this point by examining ApplLimit,
- ApplicZone, and StackSpace and comparing that to the minimum size we told
- MultiFinder we needed. This did not work well because it assumed too much about
- the relationship between what we asked MultiFinder for and what we would actually
- get back, as well as how to measure it. Instead, we will use an alternate
- method comprised of two steps. */
-
- /* It is better to first check the size of the application heap against a value
- that you have determined is the smallest heap the application can reasonably
- work in. This number should be derived by examining the size of the heap that
- is actually provided by MultiFinder when the minimum size requested is used.
- The derivation of the minimum size requested from MultiFinder is described
- in Sample.h. The check should be made because the preferred size can end up
- being set smaller than the minimum size by the user. This extra check acts to
- insure that your application is starting from a solid memory foundation. */
-
- if ((long) GetApplLimit() - (long) ApplicZone() < kMinHeap) BigBadError(eSmallSize);
-
- /* Next, make sure that enough memory is free for your application to run. It
- is possible for a situation to arise where the heap may have been of required
- size, but a large scrap was loaded which left too little memory. To check for
- this, call PurgeSpace and compare the result with a value that you have determined
- is the minimum amount of free memory your application needs at initialization.
- This number can be derived several different ways. One way that is fairly
- straightforward is to run the application in the minimum size configuration
- as described previously. Call PurgeSpace at initialization and examine the value
- returned. However, you should make sure that this result is not being modified
- by the scrap's presence. You can do that by calling ZeroScrap before calling
- PurgeSpace. Make sure to remove that call before shipping, though. */
-
- /* ZeroScrap(); */
-
- PurgeSpace(&total, &contig);
- if (total < kMinSpace)
- if (UnloadScrap() != noErr)
- BigBadError(eNoMemory);
- else {
- PurgeSpace(&total, &contig);
- if (total < kMinSpace)
- BigBadError(eNoMemory);
- }
-
- /* The extra benefit to waiting until after the Toolbox Managers have been initialized
- to check memory is that we can now give the user an alert to tell him/her what
- happened. Although it is possible that the memory situation could be worsened by
- displaying an alert, MultiFinder would gracefully exit the application with
- an informative alert if memory became critical. Here we are acting more
- in a preventative manner to avoid future disaster from low-memory problems. */
-
- menuBar = GetNewMBar(rMenuBar); /* read menus into menu bar */
- if ( menuBar == nil )
- BigBadError(eNoMemory);
- SetMenuBar(menuBar); /* install menus */
- DisposHandle(menuBar);
- AddResMenu(GetMHandle(mApple), 'DRVR'); /* add DA names to Apple menu */
- DrawMenuBar();
-
- gNumDocuments = 0;
-
- /* do other initialization here */
-
- (void) DoNew(); /* create a single empty document */
- } /*Initialize*/
-
-
- /* Used whenever a, like, fully fatal error happens */
- #pragma segment Initialize
- void BigBadError( short error )
- {
- AlertUser(error);
-
- if (gSpeechChan) /* SPEECH MANAGER: get rid of channel */
- error = DisposeSpeechChannel(gSpeechChan);
-
- ExitToShell();
- }
-
-
- /* Return a rectangle that is inset from the portRect by the size of
- the scrollbars and a little extra margin. */
-
- #pragma segment Main
- void GetTERect( WindowPtr window, Rect *teRect )
- {
- *teRect = window->portRect;
- InsetRect(teRect, kTextMargin, kTextMargin); /* adjust for margin */
- teRect->bottom = (short) (teRect->bottom - 15); /* and for the scrollbars */
- teRect->right = (short) (teRect->right - 15);
- } /*GetTERect*/
-
-
- /* Update the TERec's view rect so that it is the greatest multiple of
- the lineHeight that still fits in the old viewRect. */
-
- #pragma segment Main
- void AdjustViewRect( TEHandle docTE )
- {
- TEPtr te;
-
- te = *docTE;
- te->viewRect.bottom = (short) ((((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
- * te->lineHeight) + te->viewRect.top);
- } /*AdjustViewRect*/
-
-
- /* Scroll the TERec around to match up to the potentially updated scrollbar
- values. This is really useful when the window has been resized such that the
- scrollbars became inactive but the TERec was already scrolled. */
-
- #pragma segment Main
- void AdjustTE( WindowPtr window )
- {
- TEPtr te;
-
- te = *((DocumentPeek)window)->docTE;
- TEScroll((te->viewRect.left - te->destRect.left) -
- GetCtlValue(((DocumentPeek)window)->docHScroll),
- (te->viewRect.top - te->destRect.top) -
- (GetCtlValue(((DocumentPeek)window)->docVScroll) *
- te->lineHeight),
- ((DocumentPeek)window)->docTE);
- } /*AdjustTE*/
-
-
- /* Calculate the new control maximum value and current value, whether it is the horizontal or
- vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
- [vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
- width to the width of the viewRect. The current values are set by comparing the offset between
- the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by
- calling ShowControl. */
-
- #pragma segment Main
- void AdjustHV( Boolean isVert, ControlHandle control, TEHandle docTE, Boolean canRedraw )
- {
- short value, lines, max;
- short oldValue, oldMax;
- TEPtr te;
-
- oldValue = GetCtlValue(control);
- oldMax = GetCtlMax(control);
- te = *docTE; /* point to TERec for convenience */
- if ( isVert ) {
- lines = te->nLines;
- /* since nLines isn’t right if the last character is a return, check for that case */
- if ( *(*te->hText + te->teLength - 1) == kCrChar )
- lines += 1;
- max = (short) (lines - ((te->viewRect.bottom - te->viewRect.top) /
- te->lineHeight));
- } else
- max = (short) (kMaxDocWidth - (te->viewRect.right - te->viewRect.left));
-
- if ( max < 0 ) max = 0;
- SetCtlMax(control, max);
-
- /* Must deref. after SetCtlMax since, technically, it could draw and therefore move
- memory. This is why we don’t just do it once at the beginning. */
- te = *docTE;
- if ( isVert )
- value = (short) (te->viewRect.top - te->destRect.top) / te->lineHeight;
- else
- value = (short) (te->viewRect.left - te->destRect.left);
-
- if ( value < 0 ) value = 0;
- else if ( value > max ) value = max;
-
- SetCtlValue(control, value);
- /* now redraw the control if it needs to be and can be */
- if ( canRedraw || (max != oldMax) || (value != oldValue) )
- ShowControl(control);
- } /*AdjustHV*/
-
-
- /* Simply call the common adjust routine for the vertical and horizontal scrollbars. */
-
- #pragma segment Main
- void AdjustScrollValues( WindowPtr window, Boolean canRedraw )
- {
- DocumentPeek doc;
-
- doc = (DocumentPeek)window;
- AdjustHV(true, doc->docVScroll, doc->docTE, canRedraw);
- AdjustHV(false, doc->docHScroll, doc->docTE, canRedraw);
- } /*AdjustScrollValues*/
-
-
- /* Re-calculate the position and size of the viewRect and the scrollbars.
- kScrollTweek compensates for off-by-one requirements of the scrollbars
- to have borders coincide with the growbox. */
-
- #pragma segment Main
- void AdjustScrollSizes( WindowPtr window )
- {
- Rect teRect;
- DocumentPeek doc;
-
- doc = (DocumentPeek) window;
- GetTERect(window, &teRect); /* start with TERect */
- (*doc->docTE)->viewRect = teRect;
- AdjustViewRect(doc->docTE); /* snap to nearest line */
- MoveControl(doc->docVScroll, window->portRect.right - kScrollbarAdjust, -1);
- SizeControl(doc->docVScroll, kScrollbarWidth, (window->portRect.bottom -
- window->portRect.top) - (kScrollbarAdjust - kScrollTweek));
- MoveControl(doc->docHScroll, -1, window->portRect.bottom - kScrollbarAdjust);
- SizeControl(doc->docHScroll, (window->portRect.right -
- window->portRect.left) - (kScrollbarAdjust - kScrollTweek),
- kScrollbarWidth);
- } /*AdjustScrollSizes*/
-
-
- /* Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
- and we don't want that). If the controls are to be resized as well, call the procedure to do that,
- then call the procedure to adjust the maximum and current values. Finally re-enable the controls
- by jamming a $FF in their contrlVis fields. */
-
- #pragma segment Main
- void AdjustScrollbars( WindowPtr window, Boolean needsResize )
- {
- DocumentPeek doc;
-
- doc = (DocumentPeek) window;
- /* First, turn visibility of scrollbars off so we won’t get unwanted redrawing */
- (*doc->docVScroll)->contrlVis = kControlInvisible; /* turn them off */
- (*doc->docHScroll)->contrlVis = kControlInvisible;
- if ( needsResize ) /* move & size as needed */
- AdjustScrollSizes(window);
- AdjustScrollValues(window, needsResize); /* fool with max and current value */
- /* Now, restore visibility in case we never had to ShowControl during adjustment */
- (*doc->docVScroll)->contrlVis = kControlVisible; /* turn them on */
- (*doc->docHScroll)->contrlVis = kControlVisible;
- } /* AdjustScrollbars */
-
-
- /* Gets called from our assembly language routine, ASMCLIKLOOP, which is in
- turn called by the TEClick toolbox routine. Saves the windows clip region,
- sets it to the portRect, adjusts the scrollbar values to match the TE scroll
- amount, then restores the clip region. */
-
- #pragma segment Main
- pascal void PASCALCLIKLOOP(void)
- {
- WindowPtr window;
- RgnHandle region;
-
- window = FrontWindow();
- region = NewRgn();
- GetClip(region); /* save clip */
- ClipRect(&window->portRect);
- AdjustScrollValues(window, true); /* pass true for canRedraw */
- SetClip(region); /* restore clip */
- DisposeRgn(region);
- } /* Pascal/C ClikLoop */
-
-
- /* Gets called from our assembly language routine, ASMCLIKLOOP, which is in
- turn called by the TEClick toolbox routine. It returns the address of the
- default clikLoop routine that was put into the TERec by TEAutoView to
- ASMCLIKLOOP so that it can call it. */
-
- #pragma segment Main
- pascal ProcPtr GETOLDCLIKLOOP(void)
- {
- return ((DocumentPeek)FrontWindow())->docClik;
- } /* GetOldClikLoop */
-
-
- /* Check to see if a window belongs to the application. If the window pointer
- passed was NIL, then it could not be an application window. WindowKinds
- that are negative belong to the system and windowKinds less than userKind
- are reserved by Apple except for windowKinds equal to dialogKind, which
- mean it is a dialog.
- 1.02 - In order to reduce the chance of accidentally treating some window
- as an AppWindow that shouldn't be, we'll only return true if the windowkind
- is userKind. If you add different kinds of windows to Sample you'll need
- to change how this all works. */
-
- #pragma segment Main
- Boolean IsAppWindow( WindowPtr window )
- {
- short windowKind;
-
- if ( window == nil )
- return false;
- else { /* application windows have windowKinds = userKind (8) */
- windowKind = ((WindowPeek) window)->windowKind;
- return (windowKind == userKind);
- }
- } /*IsAppWindow*/
-
-
- /* Check to see if a window belongs to a desk accessory. */
-
- #pragma segment Main
- Boolean IsDAWindow( WindowPtr window )
- {
- if ( window == nil )
- return false;
- else /* DA windows have negative windowKinds */
- return ((WindowPeek) window)->windowKind < 0;
- } /*IsDAWindow*/
-
-
- /* Check to see if a given trap is implemented. This is only used by the
- Initialize routine in this program, so we put it in the Initialize segment.
- The recommended approach to see if a trap is implemented is to see if
- the address of the trap routine is the same as the address of the
- Unimplemented trap. */
- /* 1.02 - Needs to be called after call to SysEnvirons so that it can check
- if a ToolTrap is out of range of a pre-MacII ROM. */
-
- #pragma segment Initialize
- Boolean TrapAvailable( short tNumber, TrapType tType )
- {
- if ( ( tType == (unsigned char) ToolTrap ) &&
- ( gMac.machineType > envMachUnknown ) &&
- ( gMac.machineType < envMacII ) ) { /* it's a 512KE, Plus, or SE */
- tNumber = tNumber & 0x03FF;
- if ( tNumber > 0x01FF ) /* which means the tool traps */
- tNumber = _Unimplemented; /* only go to 0x01FF */
- }
- return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
- } /*TrapAvailable*/
-
-
- /* Display an alert that tells the user an error occurred, then exit the program.
- This routine is used as an ultimate bail-out for serious errors that prohibit
- the continuation of the application. Errors that do not require the termination
- of the application should be handled in a different manner. Error checking and
- reporting has a place even in the simplest application. The error number is used
- to index an 'STR#' resource so that a relevant message can be displayed. */
-
- #pragma segment Main
- void AlertUser( short error )
- {
- short itemHit;
- Str255 message;
-
- SetCursor(&qd.arrow);
- /* type Str255 is an array in MPW 3 */
- GetIndString(message, kErrStrings, error);
- ParamText(message, "\p", "\p", "\p");
- itemHit = Alert(rUserAlert, nil);
- } /* AlertUser */
-
- /* maintains globals for detecting double-clicks, triple-clicks, and beyond */
- /* returns current state of gClickMultiple */
-
- #pragma segment Main
- long TrackMultipleClicks(long when, Point where)
- {
- if ((when - gLastClickTickCount) < GetDblTime())
- {
- if ((abs(where.h - gLastClickWhere.h) < 5)
- && (abs(where.v - gLastClickWhere.v) < 5))
- gClickMultiple++; /* a repeat click has occurred */
- else
- {
- gClickMultiple = 1; /* reset if click is outside slop rectangle */
- gLastClickWhere = where; /* latch new coord for subsequent clicks */
- }
- }
- else
- {
- gClickMultiple = 1; /* reset if interval > DoubleTime */
- gLastClickWhere = where;
- }
-
- gLastClickTickCount = when; /* restart timer for next click */
-
- return gClickMultiple;
- }
-
- /* computes character offset of specified line start */
-
- #pragma segment Main
- short TELineStartOffset( TEHandle te, short theLine)
- {
- short charOffset;
-
- if (!te) /* no TERecord to work with */
- return (-1);
-
- if (theLine < 0) /* first char of TERecord */
- charOffset = 0;
- else if (theLine >= (*te)->nLines)
- charOffset = (*te)->teLength; /* just past last char of TERecord */
- else
- charOffset = (*te)->lineStarts[theLine];
-
- return charOffset;
- }
-
- /* compute the line in which a particular character offset falls */
- /* returns (-1) if charOffset < 0 and (*te)->nLines if charOffset */
- /* is beyond end of text */
-
- #pragma segment Main
- short TECharOffsetToLine(TEHandle te, short charOffset)
- {
- short theLine;
- short i;
-
- if (!te) /* no TERecord to work with */
- return (-1);
-
- if (charOffset < 0)
- theLine = -1;
- else if (charOffset >= (*te)->teLength)
- theLine = (*te)->nLines;
- else if ((*te)->nLines == 1) /* only one line */
- theLine = 0;
- else
- {
- theLine = (*te)->nLines; /* assume we are off the end */
-
- for (i = 0; i < (*te)->nLines; i++)
- {
- if (charOffset >= (*te)->lineStarts[i] && charOffset < (*te)->lineStarts[i+1])
- {
- theLine = i; /* this is the line we want */
- break;
- }
- }
- }
-
- return theLine;
- }
-
- /* selects the specified line in a TERecord. If theLine is beyond end of text, */
- /* the insertion point is positioned after the last char. If theLine is negative, */
- /* the insertion point is placed before the first char. */
-
- #pragma segment Main
- void TESelectLine(TEHandle te, short theLine)
- {
- short sIndex;
- short eIndex;
-
- if (!te) /* nothing to select */
- return;
-
- sIndex = TELineStartOffset(te, theLine);
- eIndex = TELineStartOffset(te, theLine + 1);
-
- TESetSelect(sIndex, eIndex, te);
- }
-
- /* Speak some text in the text edit record */
-
-
-
-
- #pragma segment Main
- OSErr SpeakTextRange(TEHandle te, short sIndex, short eIndex)
- {
- long i;
- long len;
- Ptr textPtr;
- OSErr err = noErr;
-
- if (!te) /* no TERecord to work with */
- return nilHandleErr;
-
- textPtr = *((*te)->hText); /* get ptr to TERecord text */
-
- len = eIndex - sIndex; /* total length of text to speak */
-
- if (len <= 0) /* nothing to speak */
- return noErr;
-
- if (len > 4095)
- len = 4095;
-
- for (i = 0; i < len; i++) /* copy over the chars we want to speak */
- gTextBuf[i] = textPtr[sIndex + i];
-
- gTextBufLen = (unsigned short) len;
-
- if (len > 255)
- len = 255; /* limit length to Str255 max for now */
-
- for (i = 1; i <= len; i++) /* copy over the chars we want to speak */
- gSpeechStr[i] = textPtr[sIndex + i - 1];
-
- gSpeechStr[0] = (unsigned char) len;
-
- if (!gSpeechChan)
- {
- if (voiceSel == 0)
- err = NewSpeechChannel(0, &gSpeechChan);
- else
- err = NewSpeechChannel(&vspec[voiceSel], &gSpeechChan);
-
- if (err != noErr)
- AlertUser(eSpeechManager); /* some kind of error */
- }
-
- if (err == noErr)
- {
- if (!SpeechBusy())
- {
- err = SpeakBuffer(gSpeechChan, (Ptr) gTextBuf, gTextBufLen, 0);
-
- if (err != noErr)
- AlertUser(eSpeechManager); /* some kind of error */
- }
- else
- SysBeep(10);
- }
-
- return err;
- }
-
-
-
-
-
- #pragma segment Main
- void StartupSpeech( void )
- {
- OSErr err;
- short voiceCount;
- short i;
- VoiceDescription vd;
- MenuHandle voiceMenu;
- NumVersion ver = SpeechManagerVersion();
-
- #ifndef forRez
- enum {development=0x20, alpha=0x40, beta=0x60, final=0x80, release=0x80};
- #endif
-
- #define kMgrMajorVersion 1 /* 8-bits */
- #define kMgrMinorVersion 0 /* 4-bits */
- #define kMgrBugFixVersion 0 /* 4-bits */
- #define kMgrDevelopmentStage alpha /* 8-bits */
- #define kMgrNonRelVersion 2 /* 8-bits */
-
- #define kSpeechManagerVersion ((kMgrMajorVersion << 24) \
- | (kMgrMinorVersion << 20) \
- | (kMgrBugFixVersion << 16) \
- | (kMgrDevelopmentStage << 8) \
- | (kMgrNonRelVersion))
-
- voiceMenu = GetMHandle (mVoice);
-
- err = CountVoices(&voiceCount);
- if (voiceCount > kMaxVoices-1) voiceCount = kMaxVoices-1;
- for (i = 1; i <= voiceCount; i++)
- {
- err = GetIndVoice(i, &vspec[i]);
-
- if ( true || ((*( unsigned long *) (&ver)) >= kSpeechManagerVersion))
- { // newer versions expect you to pass length in parameter list
- err = GetVoiceDescription(&vspec[i], &vd, sizeof(VoiceDescription));
- }
- else
- {
- //vd.length = sizeof(VoiceDescription); // early versions require you to set length field
- //err = GetVoiceDescription(&vspec[i], &vd);
- }
- AppendMenu (voiceMenu, vd.name);
- }
- voiceSel = 0; // use Default voice
- }
-
-